package com.paradogs.framework.server;

import com.paradogs.common.utils.EventUtils;
import com.paradogs.common.utils.ProtoUtils;
import com.paradogs.common.utils.SpringUtils;
import com.paradogs.framework.annotations.ParadogsAnnotationsHolder;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * @author: yumi
 * @date: 2024/10/8  10:25
 * @Description: TODO
 */
@Slf4j
@ChannelHandler.Sharable
public class ParadogsWSMessageHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {

        try {
            if (frame instanceof TextWebSocketFrame) {
                /** 文本帧 */

            } else if (frame instanceof BinaryWebSocketFrame) {
                /** 二进制帧 */
                ByteBuf buffer = frame.content();
                byte[] bytes = new byte[buffer.readableBytes()];
                buffer.readBytes(bytes);

                // 解码
                ByteMessage byteMessage = ProtoUtils.decode(bytes, ByteMessage.class);

                // bind temp 待优化
                // handle before
                Method before = ParadogsAnnotationsHolder.PRHandlerBeforeMap.get(byteMessage.getHeader().getRoute());
                if (before != null) {
                    Object handlerObject = SpringUtils.getBean(before.getDeclaringClass());
                    Class<?>[] beforeParameterTypes = before.getParameterTypes();
                    switch (beforeParameterTypes.length) {
                        case 0: before.invoke(handlerObject); break;
                        case 1: before.invoke(handlerObject, ProtoUtils.decode(byteMessage.getDataBytes(), beforeParameterTypes[0])); break;
                        case 2: before.invoke(handlerObject, ProtoUtils.decode(byteMessage.getDataBytes(), beforeParameterTypes[0]), ctx.channel()); break;
                    }
                }

                if (!ctx.channel().hasAttr(AttributeKey.valueOf("actor_id"))) {
                    log.error("错误操作：消息处理前未绑定 actor_id");
                    return;
                }
                Object actorId = ctx.channel().attr(AttributeKey.valueOf("actor_id")).get();

                // 加入逻辑线程
                ParadogsGameLogicHandleThread.join(actorId.toString(), new ParadogsContext(byteMessage.getHeader().getRoute(), ctx.channel(), byteMessage, null));

            } else if (frame instanceof CloseWebSocketFrame) {
                /** 关闭帧 */
                log.warn("ws close ...");
            }

        } catch (Exception e) {
            log.error("{}", e);
        }

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("Client Connect: {}", ctx.channel().remoteAddress());
//        Channel channel = ctx.channel();
//        if (!channel.hasAttr(AttributeKey.valueOf("msgQueue"))) {
//            channel.attr(AttributeKey.valueOf("msgQueue")).set(new LinkedBlockingQueue<>());
//        }
        EventUtils.publishEvent(new ServerEvent.ClientEnterEvent(ctx.channel()));
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("Client Disconnect: {}", ctx.channel().remoteAddress());
        EventUtils.publishEvent(new ServerEvent.ClientLeaveEvent(this, ctx.channel()));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
        log.error("{}", e.getMessage());
    }

}
